package com.kly.smartbi.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kly.smartbi.common.ErrorCode;
import com.kly.smartbi.constant.UserContext;
import com.kly.smartbi.exception.BusinessException;
import com.kly.smartbi.mapper.PostThumbMapper;
import com.kly.smartbi.model.entity.Post;
import com.kly.smartbi.model.entity.PostThumb;
import com.kly.smartbi.model.entity.User;
import com.kly.smartbi.model.vo.PostVO;
import com.kly.smartbi.model.vo.UserVO;
import com.kly.smartbi.service.PostService;
import com.kly.smartbi.service.PostThumbService;
import com.kly.smartbi.service.UserService;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import static com.kly.smartbi.constant.PostRedisConstant.POST_THUMB_CACHE_KEY;

/**
 * 帖子点赞服务实现
 *
 * @author <a href="https://github.com/liyupi">程序员鱼皮</a>
 * @from <a href="https://yupi.icu">编程导航知识星球</a>
 */
@Service
public class PostThumbServiceImpl extends ServiceImpl<PostThumbMapper, PostThumb>
        implements PostThumbService {

    @Resource
    private PostService postService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserService userService;


    /**
     * 点赞
     *
     * @param postId
     * @param loginUser
     */
    @Override
    public void doPostThumb(long postId, User loginUser) {
        // 判断实体是否存在，根据类别获取实体
        Post post = postService.getById(postId);
        if (post == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        // 是否已点赞
        // 每个用户串行点赞
    //     使用redis set 实现
        String key = POST_THUMB_CACHE_KEY + postId;
        // 判断是否点赞
        String userId = String.valueOf(loginUser.getId());
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId);
        // 已点赞，取消点赞
        if(Boolean.TRUE.equals(isMember)) {
            //     数据库 -1
            boolean update = postService.lambdaUpdate()
                    .eq(Post::getId, postId)
                    .setSql("thumbNum = thumbNum - 1")
                    .update();
            //     移除数据 remove redis
            if (update) {
                stringRedisTemplate.opsForSet().remove(key, userId);
            }
        }else {
            // 未点赞，点赞
            //     数据库 + 1
            boolean update = postService.lambdaUpdate()
                    .eq(Post::getId, postId)
                    .setSql("thumbNum = thumbNum + 1")
                    .update();
            if (update) {
                stringRedisTemplate.opsForSet().add(key, userId);
            }
        }
    }

    @Override
    public void setHasThumb(PostVO postVO) {
        Long postId = postVO.getId();
        String key = POST_THUMB_CACHE_KEY + postId;
        // 判断是否点赞
        Long userId = UserContext.getUser().getId();
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        postVO.setHasThumb(Boolean.TRUE.equals(isMember));
    }

    @Override
    public void doPostThumbByZSet(long postId) {
    //     使用zset查询分数
        String key = POST_THUMB_CACHE_KEY + postId;
        String userId = UserContext.getUser().getId().toString();
        Double score = stringRedisTemplate.opsForZSet().score(key, userId);
        //     判断是否点赞
        if (score == null) {
            // 未点赞，点赞
            //     数据库 + 1
            boolean update = postService.lambdaUpdate()
                    .eq(Post::getId, postId)
                    .setSql("thumbNum = thumbNum + 1")
                    .update();
            if (update) {
                // 分数时时间戳
                stringRedisTemplate.opsForZSet().add(key, userId, System.currentTimeMillis());
            }
        } else {
            // 已点赞，取消点赞
            boolean update = postService.lambdaUpdate()
                    .eq(Post::getId, postId)
                    .setSql("thumbNum = thumbNum - 1")
                    .update();
            if (update) {
                // 分数时时间戳
                stringRedisTemplate.opsForZSet().remove(key, userId);
            }
        }
    }

    @Override
    public void setHasThumbByZSet(PostVO postVO) {
        Long postId = postVO.getId();
        String key = POST_THUMB_CACHE_KEY + postId;
        Long userId = UserContext.getUser().getId();
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
        postVO.setHasThumb(score != null);
    }

    @Override
    public List<UserVO> getUserThumbRank(Long postId) {
        String key = POST_THUMB_CACHE_KEY + postId;
        Set<String> idList = stringRedisTemplate.opsForZSet().range(key, 0, 10);
        if(CollUtil.isEmpty(idList)) {
            return Collections.emptyList();
        }
        String idStr = String.join(",", idList);
        List<User> userList = userService.lambdaQuery()
                .in(User::getId, idList)
                .last(String.format("order by FIELD (id, %s)", idStr))
                .list();
        return userService.getUserVO(userList);
    }


    /**
     * 封装了事务的方法
     *
     * @param userId
     * @param postId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int doPostThumbInner(long userId, long postId) {
        PostThumb postThumb = new PostThumb();
        postThumb.setUserId(userId);
        postThumb.setPostId(postId);
        QueryWrapper<PostThumb> thumbQueryWrapper = new QueryWrapper<>(postThumb);
        PostThumb oldPostThumb = this.getOne(thumbQueryWrapper);
        boolean result;
        // 已点赞
        if (oldPostThumb != null) {
            result = this.remove(thumbQueryWrapper);
            if (result) {
                // 点赞数 - 1
                result = postService.update()
                        .eq("id", postId)
                        .gt("thumbNum", 0)
                        .setSql("thumbNum = thumbNum - 1")
                        .update();
                return result ? -1 : 0;
            } else {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
        } else {
            // 未点赞
            result = this.save(postThumb);
            if (result) {
                // 点赞数 + 1
                result = postService.update()
                        .eq("id", postId)
                        .setSql("thumbNum = thumbNum + 1")
                        .update();
                return result ? 1 : 0;
            } else {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
        }
    }

}




